home *** CD-ROM | disk | FTP | other *** search
/ BBS in a Box 5 / BBS in a Box -Volume V (BBS in a Box) (April 1992).iso / Files / Prog / M / MacOberon.cpt / MacOberonDoc.sit / OG3.Text (.txt) < prev    next >
Encoding:
Oberon Text  |  1990-11-07  |  27.9 KB  |  516 lines  |  [.Ob./.Ob*]

  1. Syntax16.Scn.Fnt
  2. Syntax10.Scn.Fnt
  3. Syntax14.Scn.Fnt
  4. Syntax12i.Scn.Fnt
  5. Syntax12.Scn.Fnt
  6. Syntax12b.Scn.Fnt
  7. Guide for Programmers of new Frame Classes and Viewer Types
  8. Frames as Active Objects
  9. Frames are the basic entities of Oberon's display system. They are more-or-less autonomous rectangular
  10. areas on the display screen and may be nested. In particular, the display screen is hierarchically organized
  11. like this: Display > tracks > viewers > data frames. Display, tracks, and viewers are entities to be managed by
  12. the viewer handler module Viewers. Because of Oberon's tiling approach, all of these frames are totally
  13. visible or totally invisible, i.e. there is no partial overlapping on this level. There exists a class of standard
  14. viewers called menu-viewers (and handled by module MenuViewers). Every menu-viewer contains exactly
  15. two vertically adjacent data frames: A header frame (including title and menu) and a main data frame. The
  16. main frame of a menu-viewer can be regarded as a virtual display terminal representing the user interface
  17. to a certain application or task. It may itself be nested, i.e. contain further subframes. Because an individual
  18. command interpreter is associated with every frame, implementing a new class of data frames is a most
  19. powerful way of adding functionality to the Oberon system.
  20. We have intentionally used the term class. As a matter of fact, frames are implemented in Oberon as active
  21. objects in the sense of object-oriented programming. Calls of frame-specific handling procedures are made
  22. by system routines in the form of message transfers. In particular, the central Oberon loop typically initiates
  23. reactions on user-input actions by sending appropriate messages to the respective viewers. We emphasize
  24. that employing the object-oriented programming paradigm in connection with the handling of frames is
  25. necessary in order to enable the kernel to call command interpreters whose identity is unknown to it.
  26. Any handler that is associated with a certain viewer class, for example menu-viewers, is expected to handle
  27. messages from (at least) three different originators: From the viewer manager, from the document
  28. manager, and from the central loop. Messages from the viewer manager report on the state change of the
  29. viewer. For example, the viewer manager Viewers sends a message whenever a hidden viewer becomes
  30. visible (and must therefore restore its contents), or when the size of a viewer has changed because its lower
  31. neighbour was opened, changed, or closed. Messages from the document manager remind the viewer of
  32. updating its contents after an editing operation. Finally, messages from module Oberon notify the viewer of
  33. system-wide events, such as input events (for example, "mouse button i pressed at position X, Y") or an
  34. inquiry for the most recent text selection (regarded in Oberon as the standard input). The interpreter-part
  35. handling input events should be regarded as an editor that is bound to the viewer class. In the case of
  36. viewers displaying text, this is a text editor, in the case of graphics, it is a graphics editor, and in the case of a
  37. viewer representing a mailbox it is an editor for electronic mail.
  38. A typical viewer handler (like the one associated with menu-viewers) processes viewer-oriented messages
  39. directly and delegates the handling of the more data-specific messages by passing them on to the viewer's
  40. subframes. Prime examples of viewer-oriented messages are requests to restore a viewer or change its size.
  41. Examples of data-specific messages are selecting an object or invoking a command by pointing to its name.
  42. Notice that new and finer-grained requests may evolve from processing of viewer-oriented message.s For
  43. example, a request to change the size of a menu-viewer is resolved in requests to change the size of one or
  44. both of its subframes. In summarizing, viewer handlers normally act both as mediators and originators of
  45. messages to be processed by the handlers of their subframes.
  46. The Canonical Decomposition of an Application
  47. Modularizing by separation of concerns is one of the most effective techniques applied to the design of large
  48. software systems. Our concept of a class of frames displaying data of a certain kind provides a perfect
  49. opportunity to demonstrate this technique. We can extract the following separate topics from the program
  50. implementing an interactive application: the tool package, the command interpreter, the display handler, and
  51. the data manager. The following tasks are assigned to these parts: Providing a collection of powerful and
  52. customized commands operating on the data of the given kind (tool package), reacting on input actions
  53. within the associated display frame (command interpreter), displaying the visible objects (display handler),
  54. and encapsulating the management of the data without any concern of how they are displayed (data
  55. manager). Typically, the data part comprises a set of editing operations. It maintains an internal data
  56. structure describing the current state of the data.
  57. It is natural to try to assign a separate module to each of these parts. We soon see that the command
  58. interpreter part and the display manager are preferably combined in one module because both need to
  59. operate on the same associated display frame. This leads to the following canonical module configurations
  60. in the casess of standard texts, graphics, pictures, and MyData, for example:
  61. Tool Package    Edit    Draw    Paint    MyTool
  62. Comm. Int. & Disp. Handler    TextFrames    GraphicFrames    PictureFrames    MyFrames
  63. Data Manager    Texts    Graphics    Pictures    MyData
  64. Notice that the same data manager can in principle be used by different display handlers and command
  65. interpreters. Data managers serve as links between different applications and thus enable an optimal
  66. integration. For example, if the graphic system is based on module Texts for the management of text
  67. captions, then text can freely be exchanged between frames of these classes: Graphic frames and text frames
  68. are integrated.
  69. Tutorial Example: Text Viewers
  70. So far, we have not explained yet how objects and messages are realized in the Oberon language. In contrast
  71. to typical object-oriented programming systems, the complete set of messages understood by an object
  72. need not be specified together with the definition of the object class. Instead, Oberon explores a more
  73. flexible dual approach: Messages are typically defined in sender modules. For example, messages of the first
  74. of the above mentioned kinds are defined in module Viewers, messages of the second kind are defined in
  75. the respective editor module (TextFrames in the case of text), and messages of the third kind are defined in
  76. module Oberon which detects input events.
  77. Roughly speaking, the Oberon language supports subclassing (by the type extension facility), but messages
  78. and message handlers are not institutionalized in the form of a language construct. We have implemented
  79. the above outlined scheme by making use of procedure variables, and by applying Oberon's record
  80. extension facility to objects and to messages.
  81. We shall now exemplify this model with the help of standard text-viewers, i.e. menu-viewers whose
  82. subframes (menu and main) are text frames. The following exposition may serve as a tutorial and template
  83. for implementors of arbitrary frame classes or viewer types. We recall that the display data structure is a
  84. hierarchy of display frames. Restricting our explanations to text-viewers we have the following hierarchy of
  85. types:
  86. Viewers.Track MenuViewers.Viewer
  87.              ^        ^
  88.       Viewers.Viewer    TextFrames.Frame
  89.                           ^                ^
  90.                          Display.Frame
  91. Module Display features the base types of frames and frame messages and the type of the message handler.
  92. The (empty) base message serves as a root in the (potentially unlimited) hierarchy of messages to be
  93. accepted by frames. Module Viewers provides the definition of type Viewer, which is an extension (variant)
  94. of type Display.Frame. Menu-viewers are a based on Viewers.Viewer an defined in module MenuViewers.
  95. In definition of Display:
  96.     TYPE
  97.       Frame = POINTER TO FrameDesc;
  98.       FrameMsg = RECORD END;
  99.       Handler = PROCEDURE (Frame, VAR FrameMsg);
  100.       FrameDesc = RECORD
  101.          dsc, next: Frame; (*son, brother*)
  102.          X, Y, W, H: INTEGER;
  103.          handle: Handler
  104.       END;
  105. In the definition of Viewers:
  106.     TYPE
  107.      Viewer = POINTER TO ViewerDesc;
  108.       ViewerDesc = RECORD (Display.FrameDesc)
  109.         state: INTEGER
  110.       END;
  111. In the definition of MenuViewers:
  112.     TYPE
  113.       Viewer = POINTER TO ViewerDesc;
  114.       ViewerDesc = RECORD (Viewers.ViewerDesc)
  115.         menuH: INTEGER
  116.       END;
  117. The following are the declarations of messages that are expected to be handled by every viewer. Note that
  118. they are distributed over several modules rather than concentrated in one place (as it would be the case in
  119. an "ordinary" object-oriented model).
  120. In definition of Viewers:
  121.       ViewerMsg = RECORD (Display.FrameMsg)
  122.         id: INTEGER;
  123.         X, Y, W, H: INTEGER; (*new rectangle*)
  124.         state: INTEGER (*new state*)
  125.       END;
  126.       (*signals change of viewer state
  127.         id = 0: restore viewer
  128.               1: modify size at bottom
  129.               2: suspend viewer*)
  130. In definition of TextFrames:
  131.       UpdateMsg = RECORD (Display.FrameMsg)
  132.          id: INTEGER; (*operation*)
  133.          text: Texts.Text; (*edited text*)
  134.          beg, end: LONGINT (*stretch*)
  135.       END;
  136.       (*signals change of contents
  137.          id = 0: stretch [beg, end[ replaced
  138.                1: stretch [beg, end[ inserted
  139.                2: stretch [beg, end[ deleted*)
  140. In definition of Oberon:
  141.         InputMsg = RECORD (Display.FrameMsg)
  142.             id: INTEGER; (*operation*)
  143.             modes, keys: SET; (*mouse*)
  144.             X, Y: INTEGER; (*position*)
  145.             ch: CHAR (*character read*)
  146.         END;
  147.         (*signals input event
  148.           id = 0: track mouse
  149.                 1: consume character*)
  150.         ControlMsg = RECORD (Display.FrameMsg)
  151.            id: INTEGER; (*operation*)
  152.            X, Y: INTEGER (*current location of the mous*)
  153.         END;
  154.          (*signals control action
  155.              id = 0: remove focus
  156.                    1: remove all marks
  157.                    2: : mark*)
  158.         SelectionMsg = RECORD (Display.FrameMsg)
  159.               time: LONGINT;
  160.               text: Texts.Text;
  161.               beg, end: LONGINT
  162.         END;
  163.          (*signals inquiry for most recent text selection*)
  164.          CopyOverMsg = RECORD (Display.FrameMsg)
  165.              text: Texts.Text;
  166.              beg, end: LONGINT
  167.          END;
  168.          (*receive text stretch [beg, end[*)
  169.           CopyMsg* = RECORD (Display.FrameMsg)
  170.               F: Display.Frame
  171.           END;
  172.           (*request for the creation of a copy of a text frame*)
  173. We recall that high-level viewer managers like MenuViewers typically pass on most of the received messages
  174. to their subframes. In the course of (pre-)processing viewer-oriented requests, high-level viewer managers
  175. can also produce new messages. In the case of MenuViewers these are
  176.            ModifyMsg* = RECORD (Display.FrameMsg)
  177.               id: INTEGER; (*operation*)
  178.               dY, Y, H: INTEGER (*vector dY, new Y and H*)
  179.            END;
  180.            (*vertically move and extend or reduce frame at bottom*)
  181.                id = 0: extend frame
  182.                id = 1: reduce frame*)
  183. dY represents a vertical translation vector showing upwards in the extend-case and showing downwards in
  184. the reduce-case. By definition, dY is always non-negative. Y and H specify the new position and height
  185. respectively.
  186. In summary, essentially the same messages have to be handled by a viewer and its subframes, except that
  187. some of the messages are processed or preprocessed by the ancestor viewer already which, in turn, may
  188. result in sending new and finer-grained messages to its subframes.
  189. A message is sent to a specific object simply by calling its installed handler. For example, F.handle(F, M) has
  190. the effect of sending message M to frame F. In case of text-frames, the installed handler is Handle. It is
  191. elaborated in module TextFrames. We now provide a tutorial sketch of this procedure. Notice that Handle
  192. makes extensive use of Oberon's type test (and type guard) facility in order to discriminate among the
  193. different message kinds.
  194. 1  PROCEDURE Handle (F: Display.Frame; VAR M: Display.FrameMsg);
  195. 2      VAR F1: Frame;
  196. 3  BEGIN
  197. 4    WITH F: Frame DO
  198. 5      IF M IS Oberon.InputMsg THEN
  199. 6        WITH M: Oberon.InputMsg DO
  200. 7          IF M.id = Oberon.track THEN Edit(F, M.X, M.Y, M.keys)
  201. 8          ELSIF M.id = Oberon.consume THEN
  202. 9            IF F.car # 0 THEN Write(F, M.ch, M.fnt, M.col, M.voff) END
  203. 12         END
  204. 13       END
  205. 14     ELSIF M IS Oberon.ControlMsg THEN
  206. 15       WITH M: Oberon.ControlMsg DO
  207. 16         IF M.id = Oberon.defocus THEN Defocus(F)
  208. 17          ELSIF M.id = Oberon.neutralize THEN Neutralize(F)
  209. 18          END
  210. 19        END
  211. 20     ELSIF M IS Oberon.SelectionMsg THEN
  212. 21       WITH M: Oberon.SelectionMsg DO GetSelection(F, M.text, M.beg, M.end, M.time) END
  213. 22     ELSIF M IS Oberon.CopyOverMsg THEN
  214. 23       WITH M: Oberon.CopyOverMsg DO CopyOver(F, M.text, M.beg, M.end) END
  215. 24     ELSIF M IS Oberon.CopyMsg THEN
  216. 25       WITH M: Oberon.CopyMsg DO Copy(F, F1); M.F := F1 END
  217. 26     ELSIF M IS MenuViewers.ModifyMsg THEN
  218. 27       WITH M: MenuViewers.ModifyMsg DO Modify(F, M.id, M.dY, M.Y, M.H) END
  219. 28     ELSIF M IS UpdateMsg THEN
  220. 29        WITH M: UpdateMsg DO
  221. 30          IF F.text = M.text THEN Update(F, M) END
  222. 31        END
  223. 32      END
  224. 33    END
  225. 34  END Handle;
  226. Explanations:
  227. 1    procedure of type Display.Handler
  228. 2    auxiliary variable for frame copy (line 25). Needed because M.F is base type
  229. 4    type guard; F must be a text frame
  230. 5    type test; is message an Oberon input message?
  231. 6    type guard
  232. 7    if message demands mouse tracking
  233. 8    if message demands consuming then consume a character
  234. 9    if caret is active in this text frame
  235. 14    type test; is message an Oberon control message?
  236. 15    type guard
  237. 16    if message demands removing the caret
  238. 17    if message demands removing caret and selection
  239. 20    type test; is message an Oberon selection inquiry?
  240. 21    if so, register own selection if it is newer than candidate
  241. 22    type test; is message a copy-over-message?
  242. 23    if so, copy text stretch to caret's location in this frame
  243. 24    type test; is message a copy-message?
  244. 25    if so, produce a copy of this frame (initialized to empty)
  245. 26    type test; is message a modify-message?
  246. 27    if so, modify size or position of this frame (see below)
  247. 28    type test; is message an update message?
  248. 30    if so, check if changed text is represented in this frame and then update frame
  249. We also provide the following refinement of the procedure TextFrames.Modify called in line 27 in
  250. TextFrames.Handle. It shows well how subframes of menu-viewers should react on a
  251. MenuViewers.ModifyMsg.
  252.   PROCEDURE Modify (F: Frame; id, dY, Y, H: INTEGER);
  253.   BEGIN
  254.     Mark(F, 0); RemoveMarks(F); (*remove position-bar, caret, and selection*)
  255.     IF id = MenuViewers.extend THEN
  256.        IF dY > 0 THEN (*if frame is to be moved*)
  257.          Display.CopyBlock(F.X, F.Y, F.W, F.H, F.X, F.Y + dY, 0); F.Y := F.Y + dY (*move original block*)
  258.        END;
  259.        Extend(F, Y) (*extend moved frame at its bottom*)
  260.     ELSIF id = MenuViewers.reduce THEN
  261.       Reduce(F, Y + dY); (*reduce original frame at its bottom*)
  262.       IF dY > 0 THEN (*if frame is to be moved*)
  263.         Display.CopyBlock(F.X, F.Y, F.W, F.H, F.X, Y, 0); F.Y := Y (*move reduced block*)
  264.       END
  265.     END;
  266.     IF F.H > 0 THEN Mark(F, 1) END (*restore position-bar*)
  267.   END Modify;
  268. Remember that dY is always non-negative, and notice that the reduce-part of the procedure is the exact
  269. inverse of the extend-part.
  270. Notice by the way that the above handler is used for the handling of both data-frames and standard
  271. header-frames. The two variants of text frames differ only by their background color. This fact testifies for a
  272. streamlined system design and is an example of the "today en-vogue" notion of code reusing.
  273. The attentative and experienced reader may have noticed that the module TextFrames plays two very
  274. different roles. Its first role is that of a (late-bound) handler of messages sent to frame instances. The second
  275. role is that of a library of procedures operating on text frames. Examples are Edit, Write, CopyOverShow,
  276. SetCaret, and TrackWord. To the two roles of TextFrames correspond two different ways of treating text
  277. frames, namely as active objects individually reacting on messages, and as passive rectangles to be operated on
  278. conventionally by invoking procedures. The second way of treating frames might be of importance in cases
  279. where text frames are text boxes belonging to the contents of some more complex document.
  280. Module TextFrames offers yet another choice, this time to potential implementors of handlers of subclasses
  281. of text frames: Either they implement their own handler by just adding increments, and then refer to Handle,
  282. or they compose an individual handler from the elements. For example, a designer of MailFrames might
  283. apply the first strategy. He might just add a few mail-oriented methods catching the mail-oriented
  284. messages, and then delegate all further processing to TextFrames.Handle. In contrast, an implementor of a
  285. new user-interface to text frames might prefer strategy 2.
  286. We conclude this section by explaining briefly the flow of control after, for example, a character has been
  287. typed. We assume that the focus viewer is a text viewer and that the caret is set in its main data frame. First,
  288. the central loop Oberon detects the new character in the keyboard buffer. It then sends an input
  289. consume-message to the focus-viewer which, in turn, passes on this message to its main subframe. After
  290. that, the handler (command interpreter) of this subframe locates the caret within the underlying text and
  291. subsequently calls the Insert-procedure of the text data manager Texts. On that, Insert inserts the character
  292. in the text and (up-)calls the associated notifier which, in our case, is residing in TextFrames. The notifier
  293. then sends a broadcast-message of type TextFrames.UpdateMsg to all visible viewers. Every single of these
  294. viewers passes on this message to its subframes. If a subframe understands the message and finds itself
  295. involved in this update, it restores its contents by accessing the new data, again from the data manager Texts.
  296. We now present some typical examples of implementations.
  297. Tutorial Examples 1: Frame oriented operations
  298. Display text line within text frame
  299.   PROCEDURE DisplayLine (F: Frame; L: Line; VAR R: Texts.Reader; X, Y: INTEGER; len: LONGINT);
  300.     VAR pat: Display.Pattern; NX, dx, x, y, w, h: INTEGER;
  301.   BEGIN NX := F.X + F.W;
  302.     WHILE (nextCh # CarriageReturn) & (R.fnt # NIL) DO
  303.       Display.GetChar(R.fnt.raster, nextCh, dx, x, y, w, h, pat);
  304.       IF (X + x + w <= NX) & (h # 0) THEN
  305.          Display.CopyPattern(R.col, pat, X + x, Y + y, 2)
  306.       END;
  307.        X := X + dx; INC(len); Texts.Read(R, nextCh)
  308.      END;
  309.      L.len := len + 1; L.wid := X + eolW - (F.X + F.margW);
  310.      L.eot := R.fnt = NIL; Texts.Read(R, nextCh)
  311.   END DisplayLine;
  312. where the types Line and Frame are defined as follows. Notice that Line is a private type and TextFrames.Frame
  313. is the public projection of type Frame.
  314.   Line = POINTER TO LineDesc;
  315.   LineDesc = RECORD
  316.     len: LONGINT; (*number of characters in this line*)
  317.     wid: INTEGER; (*width of line box*)
  318.     eot: BOOLEAN; (*end of text flag*)
  319.     next: Line (*next lower neighbour*)
  320.   END;
  321.   Location = RECORD
  322.     org, pos: LONGINT; (*line origin, position*)
  323.     dx, x, y: INTEGER; (*width and position of located character*)
  324.     lin: Line (*associated line*)
  325.   END;
  326.   Frame = POINTER TO FrameDesc;
  327.   FrameDesc = RECORD (Display.FrameDesc)
  328.     text: Texts.Text; (*displayed text*)
  329.     org: LONGINT; (*position in text of first displayed character*)
  330.     col: INTEGER; (*background color*)
  331.     lsp, asr, dsr: INTEGER; (*line spacing, ascender, descender*)
  332.     left, right, top, bot: INTEGER; (*margins*)
  333.     markH: INTEGER; (*margin width, position of mark*)
  334.     time: LONGINT; (*time of latest selection*)
  335.     mark, car, sel: INTEGER; (*state of mark, caret, selection*)
  336.     carloc: Location; (*caret location*)
  337.     selbeg, selend: Location (*locations of begin and end of selection*)
  338.     trailer: Line (*pointer to the associated sequence of line descriptors*)
  339.     END;
  340. Track caret
  341.   PROCEDURE TrackCaret (F: Frame; X, Y: INTEGER; VAR keysum: SET);
  342.     VAR loc: Location; keys: SET;
  343.   BEGIN
  344.     IF F.trailer.next # F.trailer THEN
  345.       LocateChar(F, X - F.X, Y - F.Y, F.carloc);
  346.       FlipCaret(F);
  347.       keysum := {};
  348.       LOOP
  349.         Input.Mouse(keys, X, Y);
  350.          IF keys = {} THEN EXIT END;
  351.          keysum := keysum + keys;
  352.          Oberon.DrawCursor(Oberon.Mouse, Oberon.Mouse.marker, X, Y);
  353.          LocateChar(F, X - F.X, Y - F.Y, loc);
  354.         IF loc.pos # F.carloc.pos THEN FlipCaret(F); F.carloc := loc; FlipCaret(F) END
  355.       END;
  356.       F.car := 1
  357.     END
  358.   END TrackCaret;
  359. where the following auxiliary procedures are used:
  360.    PROCEDURE LocateChar (F: Frame; x, y: INTEGER; VAR loc: Location);
  361.     VAR R: Texts.Reader;
  362.       pat: Display.Pattern;
  363.       pos, lim: LONGINT;
  364.       ox, dx, u, v, w, h: INTEGER;
  365.   BEGIN LocateLine(F, y, loc);
  366.     lim := loc.org + loc.lin.len - 1;
  367.     pos := loc.org; ox := F.left;
  368.     Texts.OpenReader(R, F.text, loc.org); Texts.Read(R, nextCh);
  369.     LOOP
  370.       IF pos = lim THEN dx := eolW; EXIT END;
  371.       Display.GetChar(R.fnt.raster, nextCh, dx, u, v, w, h, pat);
  372.       IF ox + dx > x THEN EXIT END;
  373.       INC(pos); ox := ox + dx; Texts.Read(R, nextCh)
  374.     END;
  375.     loc.pos := pos; loc.dx := dx; loc.x := ox
  376.   END LocateChar;
  377.   PROCEDURE LocateLine (F: Frame; y: INTEGER; VAR loc: Location);
  378.     VAR T: Texts.Text; L: Line; org: LONGINT; cury: INTEGER;
  379.    BEGIN T := F.text;
  380.     org := F.org; L := F.trailer.next; cury := F.H - F.top - F.asr; 
  381.     WHILE (L.next # F.trailer) & (cury > y + F.dsr) DO
  382.       org := org + L.len; L := L.next; cury := cury - F.lsp
  383.     END;
  384.     loc.org := org; loc.lin := L; loc.y := cury
  385.   END LocateLine;
  386.   PROCEDURE FlipCaret (F: Frame);
  387.    BEGIN
  388.     IF F.carloc.x < F.W THEN
  389.       IF (F.carloc.y >= 10) & (F.carloc.x + 12 < F.W) THEN
  390.         Display.CopyPattern(white, BigCaret, F.X + F.carloc.x, F.Y + F.carloc.y - 10, 2)
  391.       ELSIF (F.carloc.y >= 4) & (F.carloc.x + 6 < F.W) THEN
  392.         Display.CopyPattern(white, SmallCaret, F.X + F.carloc.x, F.Y + F.carloc.y - 4, 2)
  393.       END
  394.     END
  395.   END FlipCaret;
  396. Tutorial Examples 2: Text oriented operations
  397. Save text in buffer
  398.   PROCEDURE Save (T: Text; beg, end: LONGINT; B: Buffer);
  399.     VAR p, q, qb, qe: Piece; org: LONGINT;
  400.   BEGIN
  401.     IF end > T.len THEN end := T.len END;
  402.     FindPiece(T, beg, org, p);
  403.     NEW(qb); qb^ := p^;
  404.     qb.len := qb.len - (beg - org);
  405.     qb.off := qb.off + (beg - org);
  406.     qe := qb;
  407.     WHILE end > org + p.len DO 
  408.       org := org + p.len; p := p.next;
  409.       NEW(q); q^ := p^; qe.next := q; q.prev := qe; qe := q
  410.     END;
  411.     qe.next := NIL; qe.len := qe.len - (org + p.len - end);
  412.     B.last.next := qb; qb.prev := B.last; B.last := qe;
  413.     B.len := B.len + (end - beg)
  414.   END Save;
  415. where FindPiece is the following auxiliary procedure:
  416.   PROCEDURE FindPiece (T: Text; pos: LONGINT; VAR org: LONGINT; VAR p: Piece);
  417.      VAR n: INTEGER;
  418.   BEGIN
  419.     IF pos < T.org THEN T.org := -1; T.pce := T.trailer END;
  420.     org := T.org; p := T.pce; (*from cache*)
  421.     n := 0;
  422.     WHILE pos >= org + p.len DO org := org + p.len; p := p.next; INC(n) END;
  423.     IF n > 50 THEN T.org := org; T.pce := p END (*to cache*)
  424.   END FindPiece;
  425. and where the types Piece, Text, and Buffer are defined as follows. Notice that Piece is a private type, Texts.Text
  426. is the public projection of type Text, and Texts.Buffer is the public part of type Buffer.
  427.   Piece = POINTER TO PieceDesc;
  428.   PieceDesc = RECORD
  429.       f: Files.File; (*carrier file*)
  430.       off: LONGINT; (*offset in file*)
  431.       len: LONGINT; (*piece length*)
  432.       fnt: Fonts.Font; (*font*)
  433.       col: SHORTINT; (*color*)
  434.       voff: SHORTINT; (*vertical offset*)
  435.       prev, next: Piece (*links to neighbours*)
  436.    END;
  437.    Text = POINTER TO TextDesc;
  438.    Notifier = PROCEDURE (Text, INTEGER, LONGINT, LONGINT);
  439.    TextDesc = RECORD
  440.       len: LONGINT; (*text length*)
  441.       notify: Notifier; (*called after text changes*)
  442.       trailer: Piece; (*sentinel*)
  443.       org: LONGINT; (*cached origin*)
  444.       pce: Piece (*cached piece*)
  445.     END;
  446.   Buffer = POINTER TO BufDesc;
  447.   BufDesc = RECORD
  448.      len: LONGINT; (*buffer length*)
  449.      header, last: Piece (*buffered piece list*)
  450.   END;
  451. Insert contents of buffer in text
  452.   PROCEDURE Insert (T: Text; pos: LONGINT; B: Buffer);
  453.      VAR pl, pr, p, qb, qe: Piece; org: LONGINT;
  454.   BEGIN
  455.     FindPiece(T, pos, org, p); SplitPiece(p, pos - org, pr);
  456.     IF T.org >= org THEN (*adjust cache*)
  457.        T.org := org - p.prev.len; T.pce := p.prev
  458.     END;
  459.     pl := pr.prev; qb := B.header.next;
  460.     IF (qb # NIL) & (qb.f = pl.f) & (qb.off = pl.off + pl.len)
  461.        & (qb.fnt = pl.fnt) THEN pl.len := pl.len + qb.len; qb := qb.next
  462.      END;
  463.      IF qb # NIL THEN qe := B.last;
  464.         qb.prev := pl; pl.next := qb; qe.next := pr; pr.prev := qe
  465.      END;
  466.      T.len := T.len + B.len;
  467.      T.notify(T, insert, pos, pos + B.len); (*call postprocessor*)
  468.      B.last := B.header; B.last.next := NIL; B.len := 0
  469.   END Insert;
  470. where SplitPiece is
  471.  PROCEDURE SplitPiece (p: Piece; off: LONGINT; VAR pr: Piece);
  472.     VAR q: Piece;
  473.   BEGIN
  474.      IF off > 0 THEN NEW(q);
  475.        q.col := p.col; q.fnt := p.fnt;
  476.        q.len := p.len - off;
  477.        q.f := p.f; q.off := p.off + off;
  478.        p.len := off;
  479.        q.next := p.next; p.next := q;
  480.        q.prev := p; q.next.prev := q;
  481.        pr := q
  482.     ELSE pr := p
  483.     END
  484.  END SplitPiece;
  485. Literature
  486. Ceres Workstation
  487. H. Eberle. Development and Analysis of a Workstation Computer.
  488.     Diss. ETH No. 8431, 1987.
  489. B. Heeb. Design of the Processor-Board for the Ceres-2 Workstation, 
  490.     Bericht  93, Inst. f
  491. r Informatik, ETH Z
  492. rich, November 1988.
  493. Oberon Language
  494. N. Wirth. Type Extensions.
  495.     ACM Trans. on Prog. Languages and Systems, 10, 2 (April 1988), 204-214.
  496. N. Wirth. From Modula to Oberon.
  497.     Software - Practice and Experience, 18, 7, (July 1988), 661-670.
  498. N. Wirth. The Programming Language Oberon.
  499.     Software - Practice and Experience, 18, 7, (July 1988),     
  500. 671- 690.
  501. J. Gutknecht. Variations on the Role of Module Interfaces.
  502.     Structured Programming, 10, 1, (Jan. 1989), 40-46.
  503. Oberon System
  504. N. Wirth. An extensible system and a programming tool for workstation computers.
  505.     Proc. IVth South African Computer Science Symposium. Pretoria, South Africa.
  506. N. Wirth. Oberon: An Extensible Operating System for Workstations.
  507.     Proc. Euromicro Conf., Z
  508. rich, 29.8. - 1.9.1988.
  509. N. Wirth and J. Gutknecht.  The Oberon System.
  510.     Bericht 88, Inst. f
  511. r Informatik, ETH Z
  512. rich, July 1988,
  513.     and Software - Practice and Experience, 19 (1989).
  514. N. Wirth. Designing a System from Scratch.
  515.     Structured Programming, 10, 1 (Jan. 1989), 10-18.
  516.